package com.stars.easyms.rest.handler;

import com.stars.easyms.base.constant.HttpHeaderConstants;
import com.stars.easyms.base.constant.RestCodeConstants;
import com.stars.easyms.base.encrypt.EasyMsEncrypt;
import com.stars.easyms.base.http.EasyMsRequestEntity;
import com.stars.easyms.base.http.EasyMsResponseEntity;
import com.stars.easyms.base.util.ApplicationContextHolder;
import com.stars.easyms.base.util.DateTimeUtil;
import com.stars.easyms.base.util.FastJsonUtil;
import com.stars.easyms.base.util.SpringBootUtil;
import com.stars.easyms.rest.dto.EasyMsRestRequestEntity;
import com.stars.easyms.rest.exception.RestDuplicateRequestException;
import com.stars.easyms.rest.initializer.RequestMappingPathForRestInfo;
import com.stars.easyms.rest.properties.EasyMsRestProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

import javax.servlet.http.HttpServletResponse;
import java.io.*;

/**
 * <p>className: EasyMsResponseHandler</p>
 * <p>description: EasyMs的响应处理器</p>
 *
 * @author guoguifang
 * @version 1.6.2
 * @date 2020/9/5 3:26 下午
 */
@SuppressWarnings("unchecked")
public final class EasyMsResponseHandler {

    private static final Logger logger = LoggerFactory.getLogger(EasyMsResponseHandler.class);

    private final EasyMsRestProperties easyMsRestProperties;

    private HttpMessageConverter fastJsonHttpMessageConverter;

    private HttpMessageConverter mappingJackson2HttpMessageConverter;

    public EasyMsResponseHandler(EasyMsRestProperties easyMsRestProperties) {
        this.easyMsRestProperties = easyMsRestProperties;
        this.fastJsonHttpMessageConverter = FastJsonUtil.getFastJsonHttpMessageConverter();
        this.mappingJackson2HttpMessageConverter = ApplicationContextHolder.getApplicationContext().getBean(MappingJackson2HttpMessageConverter.class);
    }

    /**
     * 对返回信息进行加密，如果不需要加密则返回null
     */
    @Nullable
    Object encryptResponseEntity(@NonNull EasyMsResponseEntity responseEntity, RequestMappingPathForRestInfo requestMappingPathForRestInfo) {
        if (requestMappingPathForRestInfo.isEncrypt()) {
            return EasyMsEncrypt.encrypt(responseEntity, requestMappingPathForRestInfo.getSecret(),
                    requestMappingPathForRestInfo.getIv(), requestMappingPathForRestInfo.getEncryptResponseKey());
        }
        return null;
    }

    /**
     * 处理返回值
     */
    void handleResponse(@NonNull EasyMsRestRequestEntity restRequestEntity, @NonNull EasyMsResponseEntity responseEntity,
                        @Nullable Object encryptedResponseEntity, @NonNull HttpServletResponse response) throws Exception {

        handleResponse(restRequestEntity.getRequestEntity(), responseEntity, response);

        // 处理response
        ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
        outputMessage.getServletResponse().setStatus(HttpStatus.OK.value());
        outputMessage.getHeaders().set(HttpHeaderConstants.HEADER_KEY_EASY_MS, "true");
        write(encryptedResponseEntity != null ? encryptedResponseEntity : responseEntity, outputMessage);
        outputMessage.flush();
    }

    public void handleResponse(RestDuplicateRequestException e, @NonNull HttpServletResponse response) {
        // 记录日志信息
        logger.error(e.getMessage());

        // 封装响应体
        EasyMsResponseEntity responseEntity = new EasyMsResponseEntity();
        responseEntity.setRetCode(RestCodeConstants.DEFAULT_ERROR_CODE);
        responseEntity.setRetMsg(RestCodeConstants.DEFAULT_ERROR_MSG);
        responseEntity.setErrorDesc(e.getMessage());

        // 处理response
        ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
        outputMessage.getServletResponse().setStatus(HttpStatus.OK.value());
        outputMessage.getHeaders().set(HttpHeaderConstants.HEADER_KEY_EASY_MS, "true");
        try {
            write(responseEntity, outputMessage);
            outputMessage.flush();
        } catch (IOException ex) {
            logger.error("响应信息write失败!", ex);
        }
    }

    public void handleResponse(@NonNull EasyMsRequestEntity requestEntity) {
        handleResponse(requestEntity, null, null);
    }

    private void handleResponse(@NonNull EasyMsRequestEntity requestEntity, @Nullable EasyMsResponseEntity responseEntity,
                                @Nullable HttpServletResponse response) {
        // 获取参数信息
        String traceId = requestEntity.getTraceId();
        String requestId = requestEntity.getRequestId();
        String requestSys = requestEntity.getRequestSys();
        String responseSys = SpringBootUtil.getApplicationName();
        String responseTime = DateTimeUtil.now();

        // 将参数放入响应头中
        if (response != null) {
            response.setHeader(HttpHeaderConstants.TRACE_KEY, traceId);
            response.setHeader(HttpHeaderConstants.HEADER_KEY_REQUEST_ID, requestId);
            response.setHeader(HttpHeaderConstants.HEADER_KEY_REQUEST_SYS, requestSys);
            response.setHeader(HttpHeaderConstants.HEADER_KEY_RESPONSE_SYS, responseSys);
            response.setHeader(HttpHeaderConstants.HEADER_KEY_RESPONSE_TIME, responseTime);
        }

        // 记录请求日志
        logger.info("[接收服务-响应]-[请求地址:{}]-[请求系统:{}]-[服务系统:{}]-[请求ID:{}]-[响应时间:{}]{}.",
                requestEntity.getRequestPath(), requestSys, responseSys, requestId, responseTime,
                responseEntity == null ? "" : "-响应数据:" + responseEntity);
    }

    private void write(Object obj, HttpOutputMessage outputMessage) throws IOException {
        if (easyMsRestProperties.isFastJson()) {
            fastJsonHttpMessageConverter.write(obj, MediaType.APPLICATION_JSON, outputMessage);
        } else {
            mappingJackson2HttpMessageConverter.write(obj, MediaType.APPLICATION_JSON, outputMessage);
        }
    }

}
